#ifndef XRPL_BASICS_COUNTEDOBJECT_H_INCLUDED
#define XRPL_BASICS_COUNTEDOBJECT_H_INCLUDED

#include <xrpl/beast/type_name.h>

#include <atomic>
#include <string>
#include <utility>
#include <vector>

namespace ripple {

/** Manages all counted object types. */
class CountedObjects
{
public:
    static CountedObjects&
    getInstance() noexcept;

    using Entry = std::pair<std::string, int>;
    using List = std::vector<Entry>;

    List
    getCounts(int minimumThreshold) const;

public:
    /** Implementation for @ref CountedObject.

        @internal
    */
    class Counter
    {
    public:
        Counter(std::string name) noexcept : name_(std::move(name)), count_(0)
        {
            // Insert ourselves at the front of the lock-free linked list
            CountedObjects& instance = CountedObjects::getInstance();
            Counter* head;

            do
            {
                head = instance.m_head.load();
                next_ = head;
            } while (instance.m_head.exchange(this) != head);

            ++instance.m_count;
        }

        ~Counter() noexcept = default;

        int
        increment() noexcept
        {
            return ++count_;
        }

        int
        decrement() noexcept
        {
            return --count_;
        }

        int
        getCount() const noexcept
        {
            return count_.load();
        }

        Counter*
        getNext() const noexcept
        {
            return next_;
        }

        std::string const&
        getName() const noexcept
        {
            return name_;
        }

    private:
        std::string const name_;
        std::atomic<int> count_;
        Counter* next_;
    };

private:
    CountedObjects() noexcept;
    ~CountedObjects() noexcept = default;

private:
    std::atomic<int> m_count;
    std::atomic<Counter*> m_head;
};

//------------------------------------------------------------------------------

/** Tracks the number of instances of an object.

    Derived classes have their instances counted automatically. This is used
    for reporting purposes.

    @ingroup ripple_basics
*/
template <class Object>
class CountedObject
{
private:
    static auto&
    getCounter() noexcept
    {
        static CountedObjects::Counter c{beast::type_name<Object>()};
        return c;
    }

public:
    CountedObject() noexcept
    {
        getCounter().increment();
    }

    CountedObject(CountedObject const&) noexcept
    {
        getCounter().increment();
    }

    CountedObject&
    operator=(CountedObject const&) noexcept = default;

    ~CountedObject() noexcept
    {
        getCounter().decrement();
    }
};

}  // namespace ripple

#endif
